<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
    <title>Document</title>
</head>

<body>

</body>
<script>



    // 封装函数
    // 优点 -> 可以根据传入的参数快捷创建对象

    // 缺点:
    // 1. 通过函数创建的对象和函数没有关系(简单来说不是构造函数创建)
    // var arr = [1, 2, 3];  // new Array()
    // var obj = { a: 1, b: 2 };// new Object()

    // console.log(arr instanceof Array); // true
    // console.log(obj instanceof Object); // true

    // 2. 公有属性和方法重复创建 => 占内存



    function Cat(name, color) {
        // {}

        // 原型属性 -> 所有的实例化对象都有一个原型属性([[Prototype]],__proto__) ->指向构造函数的原型对象(浅复制) -> 所有的实例化对象都可以通过原型属性访问构造函数的原型对象
        // {}[[Prototype]] = Fn.prototype;


        // this -> {}

        // 我们可以做的操作 把需要的属性和方法绑定到实例化对象上
        this.name = name;
        this.color = color;
        // return this;
    }

    // prototype(原型对象) => 存实例化对象公共的属性和方法(构造函数的原型对象)
    // Fn.prototype = {constructor:Fn}
    Cat.prototype.species = "Cat";
    Cat.prototype.call = function () {
        console.log("喵喵");
    }
    Cat.prototype.skill = function () {
        console.log("捉老鼠");
    }

    Cat.prototype.say = function () {
        // this->指向调用此方法的实例化对象 (哪个实例化对象调用它 他就指向谁)
        // console.log("say方法被调用:", this);
        console.log(this.name, this.color);
    }

    // 构造函数Object原型对象上的属性
    Object.prototype.a = 1;
    Object.prototype.b = 2;


    // 所有的实例化对象都可以通过原型属性([[Prototype]])访问创建它的构造函数的原型对象!!!!

    // 实例化对象 访问 属性和方法时链式查找机制

    // 1. 自己有就找自己的

    // 2. 自己没有就找构造函数的原型对象
    //   Tom-> Tom[[Prototype]](Cat.prototype)

    // 3. 如果构造函数的原型对象(Cat.prototype)上也找不到 -> 找Object.prototype
    //   Tom-> Tom[[Prototype]](Cat.prototype) -> Cat.prototype[[Prototype]](Object.prototype)

    // 4. 如果Object.prototype上找不到,不在查找(null), 默认返回undefined
    // Tom -> Tom[[Prototype]](Cat.prototype) -> Cat.prototype[[Prototype]](Object.prototype) -> Object.prototype[[Prototype]](null)

    // 原型链(实例化对象的属性和方法查找时,基于原型[[Prototype]]链式查找机制) 
    // Tom -> Cat.prototype -> Object.prototype -> null

    var Tom = new Cat("Tom", "black");
    console.log(Tom);

    // 1. 自己有就找自己的
    // console.log(Tom.name, Tom.color);

    // 2. 自己没有就找构造函数的原型对象
    //   Tom-> Tom[[Prototype]](Cat.prototype)
    // console.log(Tom.species);
    // Tom.say();

    // 3. 如果构造函数的原型对象(Cat.prototype)上也找不到呢? => 找谁? (Object.prototype)
    // a. 原型对象本质也是一个对象(官方给的) -> 它也是构造函数创建的 -> Object
    // b. 所有的实例化对象都可以通过原型属性([[Prototype]])访问创建它的构造函数的原型对象!!!!

    // console.log(Cat.prototype);


    // 验证: Cat.prototype[[Prototype]] = Object.prototype;
    // console.log(Object.getPrototypeOf(Cat.prototype) === Object.prototype);

    // console.log(Tom.toString());
    // console.log(Tom.a);
    // console.log(Tom.b);

    // 4. 如果构造函数Object的原型对象(Object.prototype)上也找不到呢? => 找谁? ()
    // a. 原型对象本质也是一个对象(官方给的) -> 它也是构造函数创建的 -> Object
    // b. 所有的实例化对象都可以通过原型属性([[Prototype]])访问创建它的构造函数的原型对象!!!!

    // console.log(Object.prototype);

    // 依次推论 -> 构造函数Object.prototype上找不到, 再访问自己 -> 死查找
    // Object.prototype[[Prototype]] -> Object.prototype;

    // 如何解决?
    // 官方强制规定 Object.prototype[[Prototype]] = null;   如果Object.prototype上找不到,不在查找默认返回undefined

    // console.log(Object.getPrototypeOf(Object.prototype) === null);

    console.log(Tom.hello);



    // var Kitty = new Cat("Kitty", "pink");
    // console.log(Kitty);



    // var arr = new Array(1, 2, 3)
    // console.log(arr);
    // console.log(arr.a, arr.b);

    // var str = new String("hello")
    // console.log(str);
    // console.log(str.a, str.b);

    // var obj = new Object();
    // console.log(obj);


    // 注意: 所有的实例化对象 都可以访问构造函数Object.prototype上的属性和方法

</script>

</html>